package org.mx.dbcp;

import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.sql.DataSource;

import org.mx.StringUtils;
import org.mx.dal.DataSourceConfigBean;
import org.mx.dal.config.PasswordReader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationContext;
import org.springframework.core.env.Environment;
import org.springframework.jdbc.core.JdbcTemplate;

import com.alibaba.fastjson.JSON;

/**
 * 根据配置加载并初始化多个DBCP2的数据库连接缓冲池
 * 
 * @author john.peng
 *
 */
public class MyDbcp2DataSourceFactoryUtil implements InitializingBean, DisposableBean {
	private static final Logger logger = LoggerFactory.getLogger(MyDbcp2DataSourceFactoryUtil.class);

	@Value("#{'${my.databases:}'.split(',')}")
	private List<String> databaseDefs;
	@Value("${general.password.reader:}")
	private String passwordReaderName;

	private Map<String, Dbcp2DataSourceFactory> factories;
	private Environment env;
	private ApplicationContext context;

	/**
	 * 构造函数
	 * @param env Spring IoC 环境
	 * @param context Spring IoC上下文
	 */
	public MyDbcp2DataSourceFactoryUtil(Environment env, ApplicationContext context) {
		super();
		this.env = env;
		this.context = context;
	}

	/**
	 * 获取已经初始化好的数据库名字列表
	 * @return 数据库名字列表
	 */
	public Set<String> getDatabaseNames() {
		return factories != null ? factories.keySet() : new HashSet<>();
	}

	/**
	 * 根据指定的名字获取初始化好的数据库连接工厂
	 * @param name 名称
	 * @return 数据库连接工厂
	 */
	public Dbcp2DataSourceFactory getDataSourceFactory(String name) {
		return factories.containsKey(name) ? factories.get(name) : null;
	}

	/**
	 * 根据指定的名字获取初始化好的数据库DataSource
	 * @param name 名称
	 * @return 数据库DataSource
	 */
	public DataSource getDataSource(String name) {
		return factories.containsKey(name) ? factories.get(name).getDataSource() : null;
	}

	/**
	 * 根据指定的名字获取初始化好的Spring JDBC模版
	 * @param name 名称
	 * @return Spring JDBC模版对象
	 */
	public JdbcTemplate getJdbcTemplate(String name) {
		return factories.containsKey(name) ? new JdbcTemplate(factories.get(name).getDataSource()) : null;
	}

	@Override
	public void destroy() throws Exception {
		for (Dbcp2DataSourceFactory factory : factories.values()) {
			factory.close();
		}
		factories.clear();
		factories = null;
	}

	@Override
	public void afterPropertiesSet() throws Exception {
		factories = new HashMap<>();
		if (databaseDefs == null) {
			databaseDefs = new ArrayList<>();
		}
		if (databaseDefs.isEmpty()) {
			// 如果没有配置，则添加默认数据库
			databaseDefs.add("db");
		}
		// 根据配置加载并初始化数据库连接池
		for (String dbname : databaseDefs) {
			dbname = dbname.trim();
			if (StringUtils.isBlank(dbname)) {
				dbname = "db";
			}
			if (factories.containsKey(dbname)) {
				continue;
			}
			DataSourceConfigBean bean = createDataSourceConfigBean(env, context, dbname);
			Dbcp2DataSourceFactory factory = createDbcp2DataSourceFactory(bean);
			factories.put(dbname, factory);
		}
		if (logger.isInfoEnabled()) {
			logger.info(String.format("Init DBCP2 factory success, total: %d.", factories.size()));
		}
	}

	private DataSourceConfigBean createDataSourceConfigBean(Environment env, ApplicationContext context, String dbname) {
		PasswordReader passwordReader = null;
		if (!StringUtils.isBlank(passwordReaderName) && context.containsBean(passwordReaderName)) {
			passwordReader = context.getBean(passwordReaderName, PasswordReader.class);
		}
		return new Dbcp2DataSourceConfigBean(env, dbname, passwordReader);
	}

	private Dbcp2DataSourceFactory createDbcp2DataSourceFactory(DataSourceConfigBean dataSourceConfigBean) {
		Dbcp2DataSourceFactory factory = new Dbcp2DataSourceFactory(dataSourceConfigBean);
		try {
			factory.init();
			if (logger.isInfoEnabled()) {
				logger.info(String.format("Init DB connection pool success, context: %s.",
						JSON.toJSONString(dataSourceConfigBean)));
			}
		} catch (SQLException ex) {
			if (logger.isErrorEnabled()) {
				logger.error(String.format("Init DB connection pool fail, context: %s.",
						JSON.toJSONString(dataSourceConfigBean)), ex);
			}
		}
		return factory;
	}
}
